package org.truenewx.tnxjee.webmvc.response.exception.resolver;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
import org.truenewx.tnxjee.core.util.LogUtil;
import org.truenewx.tnxjee.service.exception.ResolvableException;
import org.truenewx.tnxjee.webmvc.method.support.HandlerMethodMapping;
import org.truenewx.tnxjee.webmvc.security.web.RestResponseResultDecider;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * 处理运行期异常至响应体中的解决器
 */
public class BodyRuntimeExceptionResolver extends AbstractHandlerExceptionResolver {

    @Autowired
    protected HandlerMethodMapping handlerMethodMapping;
    @Autowired(required = false)
    private RestResponseResultDecider restResponseResultDecider;

    public BodyRuntimeExceptionResolver() {
        setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
    }

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) {
        // 处理运行时异常中不是可解决异常的那部分异常
        if (ex instanceof RuntimeException
                && !(ex instanceof ConstraintViolationException)
                && !(ex instanceof ResolvableException)) {
            // 只处理REST请求
            if (this.handlerMethodMapping.isRestRequest(request, handler)) {
                LogUtil.error(getClass(), ex);
                try {
                    restResolveException(request, response, (RuntimeException) ex);
                } catch (IOException ignored) {
                }
                return new ModelAndView();
            }
        }
        return null;
    }

    protected void restResolveException(HttpServletRequest request, HttpServletResponse response, RuntimeException ex)
            throws IOException {
        int status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
        if (this.restResponseResultDecider != null && this.restResponseResultDecider.supports(request)) {
            this.restResponseResultDecider.resolveRuntimeException(request, response, ex);
            request.setAttribute(RestResponseResultDecider.ATTR_DEFAULT_STATUS, status);
        } else {
            response.setStatus(status);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            String message = Objects.requireNonNullElse(ex.getCause(), ex).getMessage();
            if (message == null) {
                message = "null";
            }
            response.getWriter().print(message);
        }
    }

}
